package world.snowcrystal.aa;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.log.LogMessage;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;
import world.snowcrystal.dao.UserAuthenticationDetails;
import world.snowcrystal.exception.SnowcrystalInvalidTokenException;
import world.snowcrystal.exception.SnowcrystalNoCredentialsProvidedException;
import world.snowcrystal.exception.SnowcrystalTokenExpiredException;
import world.snowcrystal.exception.SnowcrystalUserNotFoundException;
import world.snowcrystal.utils.JwtUtils;
import world.snowcrystal.utils.RequestUtils;

import java.io.IOException;


@Log4j2
public class SUsernamePasswordAuthenticationFilter
        extends UsernamePasswordAuthenticationFilter {


    public SUsernamePasswordAuthenticationFilter() {}

    public SUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }


    protected String[] retrieveTokenFromCookie(HttpServletRequest request)
            throws SnowcrystalInvalidTokenException,
            SnowcrystalTokenExpiredException {
        Cookie[] cookies = request.getCookies();
        String cookieValue = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(JwtUtils.AUTHENTICATION_COOKIE_NAME)) {
                    cookieValue = cookie.getValue();
                    request.setAttribute("AuthenticationTokenCookie", cookie);
                }
            }
        }
        if (!StringUtils.hasText(cookieValue)) {
            return null;
        }
        return JwtUtils.verifyJwt(cookieValue);
    }

    @Nullable
    @Override
    protected String obtainPassword(HttpServletRequest request) {
        String passwordParameter = getPasswordParameter();
        String password = request.getParameter(passwordParameter);
        if (!StringUtils.hasText(password)) {
            Object p = request.getAttribute(passwordParameter);
            if (p != null) {
                password = (String) p;
            }
        }
        return password;
    }

    @Nullable
    @Override
    protected String obtainUsername(HttpServletRequest request) {
        String usernameParameter = getUsernameParameter();
        String username = request.getParameter(usernameParameter);
        if (!StringUtils.hasText(username)) {
            Object p = request.getAttribute(usernameParameter);
            if (p != null) {
                username = (String) p;
            }
        }
        return username;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response)
            throws AuthenticationException {

        String username = obtainUsername(request);
        username = (username != null) ? username.trim() : "";
        String password = obtainPassword(request);
        password = (password != null) ? password : "";

        if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
            String[] strings = null;
            strings = retrieveTokenFromCookie(request);
            if (strings == null) {
                log.info(" from remote user : " + request.getRemoteAddr() + " No credentials provided");
                throw new SnowcrystalNoCredentialsProvidedException("No credentials provided");
            }
            if (strings.length != 2) {
                throw new SnowcrystalInvalidTokenException("invalid cookie");
            }
            username = strings[0];
            password = strings[1];
        }

        if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
            throw new SnowcrystalNoCredentialsProvidedException("No credentials provided");
        }

        if (!checkUserExists(username)) {
            throw new SnowcrystalUserNotFoundException("user not found");
        }

        UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
                password);
        RequestUtils.setRequestUsername(request,username);
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        Authentication authenticate = getAuthenticationManager().authenticate(authRequest);

        RequestUtils.setAuthenticatedUser(request, (UserAuthenticationDetails) authenticate.getPrincipal());
        return authenticate;
    }

    public boolean checkUserExists(String username) {
        return true;
    }


    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
        }
        if (this.eventPublisher != null) {
            this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
        getSuccessHandler().onAuthenticationSuccess(request, response, chain, authResult);

    }


}
